#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <chrono>
#include <fstream>

#include "rtsp.h"
#include "rtp.h"

RtspServer::RtspServer()
{
}

RtspServer::~RtspServer()
{
}

void RtspServer::init()
{
    // init socket
    int serv_sock;
    serv_sock = socket(PF_INET, SOCK_STREAM, 0);

    int option = 1;
    setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));

    sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port = htons(8554);
    if (bind(serv_sock, (sockaddr *)&serv_addr, sizeof(serv_addr)) == -1)
    {
        perror("bind error");
        exit(1);
    }

    if (listen(serv_sock, 15) == -1)
    {
        perror("listen error");
        exit(1);
    }

    // epoll
    _epoll_fd = epoll_create(1024);
    //memset(_ev_datas, 0, sizeof(_ev_datas));

    _ev_datas[serv_sock].fd = serv_sock;
    _ev_datas[serv_sock].epoll_fd = _epoll_fd;
    _ev_datas[serv_sock].callback = std::bind(&RtspServer::accept_connect, this, std::placeholders::_1);
    _ev_datas[serv_sock].ev_datas = _ev_datas;
    epoll_event ev_arg;
    ev_arg.events = EPOLLIN;
    ev_arg.data.ptr = &_ev_datas[serv_sock];
    epoll_ctl(_epoll_fd, EPOLL_CTL_ADD, serv_sock, &ev_arg);
}

void RtspServer::run()
{
    while (1)
    {
        int rst = epoll_wait(_epoll_fd, _events, 1024, -1);
        for (int i = 0; i < rst; i++)
        {
            m_event *data = (m_event *)_events[i].data.ptr;
            if (data && data->callback)
            {
                data->callback(data);
            }
        }
    }
}

void RtspServer::accept_connect(void *arg)
{
    if (!arg)
        return;

    m_event *data = (m_event *)arg;

    sockaddr_in clnt_addr;
    socklen_t clnt_len = sizeof(clnt_addr);

    int clnt_sock = accept(data->fd, (sockaddr *)&clnt_addr, &clnt_len);
    int flag = fcntl(clnt_sock, F_GETFL, 0);
    fcntl(clnt_sock, F_SETFL, flag | O_NONBLOCK);

    std::string clnt_ip = inet_ntoa(clnt_addr.sin_addr);
    _ipmap.insert(std::make_pair(clnt_sock, clnt_ip));

    m_event *ev_datas = data->ev_datas;
    ev_datas[clnt_sock].fd = clnt_sock;
    ev_datas[clnt_sock].epoll_fd = data->epoll_fd;
    ev_datas[clnt_sock].callback = std::bind(&RtspServer::read_data, this, std::placeholders::_1);
    ev_datas[clnt_sock].ev_datas = ev_datas;
    epoll_event ev_arg;
    ev_arg.events = EPOLLIN | EPOLLET;
    ev_arg.data.ptr = &ev_datas[clnt_sock];
    epoll_ctl(data->epoll_fd, EPOLL_CTL_ADD, clnt_sock, &ev_arg);
}

void RtspServer::read_data(void *arg)
{
    if (!arg)
        return;

    m_event *data = (m_event *)arg;
    int clnt_fd = data->fd;
    if (clnt_fd < 0)
        return;

    std::string recv_str;
    char buf[100];

    while (1)
    {
        int len = read(clnt_fd, buf, 100);
        if (len < 0)
        {
            if(errno == EAGAIN)
            {
                break;
            }
            close(clnt_fd);
            perror("read client socket error");
            return;
        }
        else if (len == 0)
        {
            data->fd = -1;
            data->message_len = 0;
            if(data->message)
            {
                delete[] data->message;
                data->message = nullptr;
            }

            _ipmap.erase(clnt_fd);
            close(clnt_fd);
            std::cout << "client fd=" << clnt_fd << " close the connection...\n"
                      << std::endl;
            return;
        }
        else
        {
            recv_str.append(buf, len);
        }
    }
    std::cout << "request>>>\n"
              << recv_str << "\n"
              << std::endl;

    std::string rtsp_method;
    std::string response_str = parse_request(recv_str, clnt_fd, &rtsp_method);
    char *message_buf = new char[response_str.size()];
    memcpy(message_buf, response_str.data(), response_str.size());

    data->callback = std::bind(&RtspServer::write_data, this, std::placeholders::_1);
    data->message = message_buf;
    data->message_len = response_str.size();
    data->rtsp_method = rtsp_method;

    epoll_event ev_arg;
    ev_arg.events = EPOLLOUT | EPOLLET;
    ev_arg.data.ptr = data;
    epoll_ctl(data->epoll_fd, EPOLL_CTL_MOD, clnt_fd, &ev_arg);
}

void RtspServer::write_data(void *arg)
{
    if (!arg)
        return;

    m_event *data = (m_event *)arg;
    int clnt_fd = data->fd;
    if (clnt_fd < 0)
        return;

    write(clnt_fd, data->message, data->message_len);
    if (data->rtsp_method == "PLAY")
    {
        _cond.notify_all();
    }

    data->callback = std::bind(&RtspServer::read_data, this, std::placeholders::_1);
    delete[] data->message;
    data->message = nullptr;
    data->message_len = 0;
    
    epoll_event ev_arg;
    ev_arg.events = EPOLLIN | EPOLLET;
    ev_arg.data.ptr = data;
    epoll_ctl(data->epoll_fd, EPOLL_CTL_MOD, clnt_fd, &ev_arg);
}

std::string RtspServer::parse_request(const std::string &str, int clnt_fd, std::string *type)
{
    std::vector<std::string> strs = split_string(str, "\r\n\r\n");
    if (strs.size() != 2)
        return "";
    std::string str_header = strs[0];
    std::string str_body = strs[1];

    std::vector<std::string> headers = split_string(str_header, "\r\n");
    if (headers.size() == 0)
        return "";
    std::string title = headers[0];

    std::map<std::string, std::string> header_map;
    for (int i = 1; i < headers.size(); i++)
    {
        std::string pHeader = headers[i];
        std::vector<std::string> pairs = split_string(pHeader, ": ");
        if (pairs.size() != 2)
            continue;
        header_map.insert(std::make_pair(pairs[0], pairs[1]));
    }

    std::vector<std::string> title_blocks = split_string(title, " ");
    if (title_blocks.size() != 3)
        return "";
    std::string method = title_blocks[0];
    std::string uri = title_blocks[1];
    std::string version = title_blocks[2];

    if (version != "RTSP/1.0")
        return "";

    *type = method;
    std::string response_str;

    if (method == "OPTIONS")
    {
        if (header_map.find("CSeq") == header_map.end())
            return "";

        response_str.append(
            "RTSP/1.0 200 OK\r\n"
            "CSeq: " + header_map["CSeq"] + "\r\n"
            "Public: OPTIONS, DESCRIBE, SETUP, PLAY\r\n\r\n");
    }
    else if (method == "DESCRIBE")
    {
        // sdp info
        std::string sdp;
        sdp.append("v=0\r\n");

        std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
        auto duration = now.time_since_epoch();
        auto duration_ms = std::chrono::duration_cast<std::chrono::microseconds>(duration);
        uint64_t timestamp = duration_ms.count();
        sdp.append("o=- ").append(std::to_string(timestamp)).append(" ").append(std::to_string(timestamp)).append(" IN IP4 192.168.0.150\r\n");

        sdp.append("s=").append(uri).append("\r\n");
        sdp.append("c=IN IP4 192.168.0.150\r\n");
        sdp.append("t=0 0\r\n");
        sdp.append("m=video 0 RTP/AVP 96\r\n");
        sdp.append("a=rtpmap:96 H264/90000\r\n");
        sdp.append("a=control:trackID=0\r\n");
        sdp.append("m=audio 0 RTP/AVP 97\r\n");
        sdp.append("a=rtpmap:97 mpeg4-generic/48000/2\r\n");
        sdp.append("a=fmtp:97 streamtype=5; profile-level-id=1; mode=AAC-hbr; config=1190; sizeLength=13; indexLength=3;\r\n");
        sdp.append("a=control:trackID=1");

        response_str.append(
            "RTSP/1.0 200 OK\r\n"
            "CSeq: " + header_map["CSeq"] + "\r\n"
            "Content-Type: application/sdp\r\n"
            "Content-Base: " + uri + "\r\n"
            "Content-Length: " + std::to_string(sdp.size()) + "\r\n\r\n" + sdp);
    }
    else if (method == "SETUP")
    {
        std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
        auto duration = now.time_since_epoch();
        auto duration_s = std::chrono::duration_cast<std::chrono::seconds>(duration);
        uint64_t uid = duration_s.count();
        std::string sessionId = std::to_string(clnt_fd) + "-" + std::to_string(uid);

        uint16_t rtp_port = 0;
        std::string pre_trans = header_map["Transport"];
        auto parts = split_string(pre_trans, ";");
        for (std::string &str : parts)
        {
            if (str.find("client_port") != std::string::npos)
            {
                auto pairs1 = split_string(str, "=");
                if (pairs1.size() != 2)
                    break;;
                auto pairs2 = split_string(pairs1[1], "-");
                if (pairs2.size() != 2)
                    break;;
                rtp_port = atoi(pairs2[0].c_str());
            }
        }

        //vedio or audio
        if(uri.find("trackID=0") != std::string::npos)
        {
            response_str.append(
            "RTSP/1.0 200 OK\r\n"
            "CSeq: " + header_map["CSeq"] + "\r\n"
            "Session: " + sessionId + "\r\n" +
            "Transport: " + pre_trans + ";server_port=" +
            std::to_string(RTP_PORT_VEDIO) + "-" + std::to_string(RTCP_PORT_VEDIO) + "\r\n\r\n");

            _h264_thread = std::make_unique<std::thread>(&RtspServer::play_h264, this, clnt_fd, rtp_port);
            _h264_thread->detach();
        }
        else if (uri.find("trackID=1") != std::string::npos)
        {
            response_str.append(
            "RTSP/1.0 200 OK\r\n"
            "CSeq: " + header_map["CSeq"] + "\r\n"
            "Session: " + sessionId + "\r\n" +
            "Transport: " + pre_trans + ";server_port=" +
            std::to_string(RTP_PORT_AUDIO) + "-" + std::to_string(RTCP_PORT_AUDIO) + "\r\n\r\n");

            _acc_thread = std::make_unique<std::thread>(&RtspServer::play_aac, this, clnt_fd, rtp_port);
            _acc_thread->detach();
        }
    }
    else if (method == "PLAY")
    {
        std::string session = header_map["Session"];
        response_str.append(
            "RTSP/1.0 200 OK\r\n"
            "CSeq: " + header_map["CSeq"] + "\r\n"
            "Range: npt=0.000-\r\n"
            "Session: " + session + ";timeout=10\r\n\r\n");
    }

    std::cout << "response>>>\n"
              << response_str << "\n"
              << std::endl;
    return response_str;
}

std::vector<std::string> RtspServer::split_string(const std::string &str, const std::string &pattern)
{
    std::vector<std::string> result;

    if (str == "")
        return result;

    std::string str2 = str;
    while (str2.find(pattern) != str2.npos)
    {
        size_t pos = str2.find(pattern);
        std::string section = str2.substr(0, pos);
        result.push_back(section);

        str2 = str2.substr(pos + pattern.size(), str2.size() - section.size() - pattern.size());
    }
    result.push_back(str2);

    return result;
}

void RtspServer::play_h264(int clnt_fd, uint16_t rtp_port)
{
    if(rtp_port == 0)
        return;

    std::unique_lock<std::mutex> lck(_mtx);
    _cond.wait(lck);
    std::cout << "start send h264 data...\n" << std::endl;
    lck.unlock();

    std::fstream file;
    file.open("/home/duxin/project/RtspServer/bin/resource/test.h264", std::ios::in | std::ios::binary);
    if (!file.is_open())
    {
        lck.lock();
        perror("open file error");
        lck.unlock();
        return;
    }

    if (_ipmap.find(clnt_fd) == _ipmap.end())
    {
        return;
    }

    RtpEmitter Rtp_emitter(RTP_PORT_VEDIO, _ipmap[clnt_fd].c_str(), rtp_port, 96);

    char *buf = new char[1024 * 1024];
    uint32_t offset = 0;
    uint32_t length = 0;
    char sign[4] = {-1, -1, -1, -1};
    while (file.peek() != EOF) // 循环读取每一个nalu并发送
    {
        file.read(buf + offset, 1);
        sign[3] = sign[2];
        sign[2] = sign[1];
        sign[1] = sign[0];
        sign[0] = *(buf + offset);
        offset++;

        if (sign[2] == 0x00 && sign[1] == 0x00 && sign[0] == 0x01)
        {
            length = (sign[3] == 0x00) ? (offset - 4) : (offset - 3);
            
            Rtp_emitter.send_nalu(buf, length, true);
            offset = 0;
            length = 0;
            std::this_thread::sleep_for(std::chrono::milliseconds(5));
        }
    } // 将剩余的数据发送
    length = offset;
    Rtp_emitter.send_nalu(buf, length, true);

    if(buf)
    {
        delete[] buf;
    }

    lck.lock();
    std::cout << "send h264 data finished.\n" << std::endl;
}

void RtspServer::play_aac(int clnt_fd, uint16_t rtp_port)
{
    if (rtp_port == 0)
        return;

    std::unique_lock<std::mutex> lck(_mtx);
    _cond.wait(lck);
    std::cout << "start send acc data...\n" << std::endl;
    lck.unlock();

    std::fstream file;
    file.open("/home/duxin/project/RtspServer/bin/resource/test.aac", std::ios::in | std::ios::binary);
    if (!file.is_open())
    {
        lck.lock();
        perror("open file error");
        lck.unlock();
        return;
    }

    if (_ipmap.find(clnt_fd) == _ipmap.end())
    {
        return;
    }

    RtpEmitter Rtp_emitter(RTP_PORT_AUDIO, _ipmap[clnt_fd].c_str(), rtp_port, 97);

    char *buf = new char[2000];
    uint32_t offset = 0;
    uint32_t length = 0;
    while (file.peek() != EOF) // 循环读取每一个adts并发送
    {
        file.read(buf + offset, 7);
        offset += 7;

        uint8_t* byte_12 = (uint8_t*)buf;
        if (byte_12[0] != 0xFF || byte_12[1] & 0xF0 != 0xF0)
        {
            return;
        }

        // bool is_crc = byte_12[1] & 0x01;
        // if(is_crc)
        // {
        //     file.read(buf + offset, 2);
        //     offset += 2;
        // }

        uint8_t* byte_456 = (uint8_t *)(buf + 3);
        uint16_t frame_length = ((byte_456[0] & 0x03) << 11) + (byte_456[1] << 3) + (byte_456[2] >> 5);

        file.read(buf + offset, frame_length - offset);

        Rtp_emitter.send_adts(buf + offset, frame_length - offset, true);
        offset = 0;

        std::this_thread::sleep_for(std::chrono::milliseconds(3));
    }

    if (buf)
    {
        delete[] buf;
    }

    lck.lock();
    std::cout << "send acc data finished.\n" << std::endl;
}
